﻿using System;
using System.Drawing;
using System.IO;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using GpgApi;
using WgpgCore;

namespace WgpgGUI
{
    public static class Photo
    {
        /// <summary>
        /// Converti une image vers un BitmapSource qui est utilisé par Wpf.
        /// </summary>
        /// <param name="img"></param>
        /// <returns></returns>
        public static BitmapSource ImageToBitmapSource(Image img)
        {
            if (img == null)
                return null;

            Bitmap b = img as Bitmap;
            IntPtr hBitmap = b.GetHbitmap();
            try
            {
                BitmapSource bs = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight(b.Width, b.Height));
                bs.Freeze();
                return bs;
            }
            finally
            {
                NativeMethods.DeleteObject(hBitmap);
            }
        }

        /// <summary>
        /// Ajoute une image à partir d'un événement Drag & Drop.
        /// </summary>
        /// <param name="owner"></param>
        /// <param name="e">L'événement contenant les informations nécessaires à l'ajout de l'image</param>
        /// <param name="keyId">L'id de la clé</param>
        /// <returns>True si tout s'est bien déroulé ; false si un problème est survenu</returns>
        public static Boolean Add(Window owner, DragEventArgs e, KeyId keyId)
        {
            return Photo.Add(owner, Photo.DropToString(e), keyId);
        }

        /// <summary>
        /// Ajoute l'image <em>image</em> à la clé.
        /// L'image est préalablement sauvegardée dans un fichier temporaire.
        /// </summary>
        /// <param name="owner"></param>
        /// <param name="image">L'image à ajouter à la clé</param>
        /// <param name="keyId">L'id de la clé</param>
        /// <returns>True si tout s'est bien déroulé ; false si un problème est survenu</returns>
        public static Boolean Add(Window owner, BitmapSource image, KeyId keyId)
        {
            String filename = Utils.GetTempFileName();
            if (!Photo.SaveBitmapSourceToFile(image, filename))
            {
                File.Delete(filename);
                return false;
            }

            Boolean result = Photo.Add(owner, filename, keyId);
            File.Delete(filename);

            return result;
        }

        /// <summary>
        /// Ajoute une photo à partir d'un fichier.
        /// Cette fonction se charge de vérifier si le fichier est bien un fichier local (sur le disque dur)
        /// et que son format est bien Jpeg. Si ce n'est pas le cas, elle fait le nécessaire.
        /// 
        /// Si le fichier est déjà un fichier local mais qu'il est inexistant, ou que le fichier est distant et n'a pas pu être téléchargé/trouvé
        /// alors une MessageBox s'affiche et la fonction retourne false.
        /// 
        /// Si le fichier n'est pas une image jpeg et que la conversion du fichier vers jpeg échoue, alors une MessageBox s'affiche
        /// et la fonction retourne false.
        /// 
        /// Une fois ces deux vérifications terminées, l'image est ajoutée à GPG et true est renvoyé si l'ajout s'est déroulé avec succès.
        /// </summary>
        /// <param name="owner"></param>
        /// <param name="filename">Le fichier contenant l'image a ajouter</param>
        /// <param name="keyId">L'id de la clé</param>
        /// <returns>True si tout s'est bien déroulé ; false si un problème est survenu</returns>
        public static Boolean Add(Window owner, String filename, KeyId keyId)
        {
            try
            {
                using (RemoteFile remotingFile = new RemoteFile(filename))
                using (JpegImage image = new JpegImage(remotingFile.LocalPath))
                {
                    GpgAddPhoto addPhoto = new GpgAddPhoto(keyId, image.JpegPath)
                    {
                        AskPassphrase = GUIUtils.AskPassphrase(owner)
                    };

                    GpgInterfaceResult result = addPhoto.Execute();

                    if (result.Status == GpgInterfaceStatus.Error)
                    {
                        switch (result.Message)
                        {
                            case GpgInterfaceMessage.BadPassphrase:
                            {
                                CustomMessageBox.Show(owner, L10N.T("bad_passphrase"), L10N.T("bad_passphrase_title"), MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
                                break;
                            }

                            case GpgInterfaceMessage.InvalidImageFormat:
                            {
                                CustomMessageBox.Show(owner, L10N.T("add_corrupted_image"), L10N.T("dialog_add_a_photo_title"), MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
                                break;
                            }
                        }
                    }

                    return result.Status == GpgInterfaceStatus.Success;
                }
            }
            catch (FileNotFoundException)
            {
                CustomMessageBox.Show(owner, L10N.T("file_not_found", filename), L10N.T("dialog_add_a_photo_title"), MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
            }
            catch (InvalidImageException)
            {
                CustomMessageBox.Show(owner, L10N.T("file_is_not_an_image", filename), L10N.T("dialog_add_a_photo_title"), MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
            }

            return false;
        }

        private static String DropToString(DragEventArgs e)
        {
            if (e == null || e.Data == null)
                return null;

            String str = null;

            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                str = ((String[])e.Data.GetData(DataFormats.FileDrop))[0];

            if (e.Data.GetDataPresent(DataFormats.Tiff))
                str = ((String[])e.Data.GetData(DataFormats.Tiff))[0];

            if (e.Data.GetDataPresent(DataFormats.StringFormat))
                str = (String)e.Data.GetData(DataFormats.StringFormat);

            if (e.Data.GetDataPresent(DataFormats.Text))
                str = (String)e.Data.GetData(DataFormats.Text);

            if (e.Data.GetDataPresent(DataFormats.UnicodeText))
                str = (String)e.Data.GetData(DataFormats.UnicodeText);

            return Photo.IsValidPath(str) ? str : null;
        }

        /// <summary>
        /// Vérifie que le Glisser-Déposé provient de quelque chose de
        /// valide et pouvant contenir une image.
        /// </summary>
        /// <param name="e">L'événement Drag à vérifier.</param>
        /// <returns>True s'il est valide ; sinon false.</returns>
        public static Boolean IsValidDrop(DragEventArgs e)
        {
            return Photo.DropToString(e) != null;
        }

        /// <summary>
        /// Checks whether a path could correspond to a valid path for a photo.
        /// </summary>
        /// <param name="path">The path to validate</param>
        /// <returns>
        /// Returns true if the path corresponds to a local file
        /// or if the path is a valid URI which begins by http or https;
        /// otherwise it returns false.
        /// </returns>
        private static Boolean IsValidPath(String path)
        {
            if (String.IsNullOrEmpty(path))
                return false;

            if (File.Exists(path))
                return true;

            if (Uri.IsWellFormedUriString(path, UriKind.Absolute))
            {
                String scheme = new Uri(path).Scheme;
                if (scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeHttps)
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Sauvegarde l'image <em>image</em> dans le fichier <em>filename</em> en utilisant le format Jpeg (seul format accepté par GPG).
        /// </summary>
        /// <param name="image">L'image à sauvegarder</param>
        /// <param name="filename">Le fichier dans lequel doit être sauvegardée l'image</param>
        /// <returns>True si l'image a pu être sauvegardée ; sinon false.</returns>
        private static Boolean SaveBitmapSourceToFile(BitmapSource image, String filename)
        {
            try
            {
                using (FileStream fileStream = new FileStream(filename, FileMode.Create))
                {
                    JpegBitmapEncoder encoder = new JpegBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(image));
                    encoder.QualityLevel = 100;
                    encoder.Save(fileStream);
                }

                return true;
            }
            catch
            {
                return false;
            }
        }
    }
}
